import { _decorator, animation, CharacterControllerContact, Collider, geometry, ITriggerEvent, Node, PhysicsSystem, randomRange, randomRangeInt, RigidBody, toDegree, toRadian, v3, Vec3 } from 'cc';
import { GamerController } from './GamerController';
import Events from '../../Scripts/Framework/Utils/Events';
import { EventName } from '../../Scripts/Framework/Const/EventName';
import { Gun } from './Items/Gun';
import { Ammo } from './Items/Ammo';
import { BodyTrigger } from './BodyTrigger';
import { nUtils } from '../Navimesh/libs/nUtils';
import { InGameManager } from './Managers/InGameManager';
import { PoisonManager } from './Managers/PoisonManager';
import { Weapon } from './Items/Weapon';
import { ItemBox } from './ItemBox';
import { PoolManager } from '../../Scripts/Framework/Managers/PoolManager';
import { Armor } from './Items/Armor';
import { GameUI } from './GameUI';
import NodeUtil from '../../Scripts/Framework/Utils/NodeUtil';
const { ccclass, property } = _decorator;

@ccclass('EnemyController')
export class EnemyController extends GamerController {

    moveState = AIMoveState.Idle;
    actionState = AIActionState.Idle;
    enemyTarget: GamerController;
    hateEnemy: GamerController;
    boxTarget: Node;
    allEnemyTargets: GamerController[] = [];
    allBoxTargets: Node[] = [];
    mapIcons: Node;

    /* 可否移动 */
    private move = false;
    /* 路线 */
    private _path: Vec3[] = [];
    /* 欧拉角度Y */
    private roleAngel = 0;
    private step = 0;
    private currstep = null;
    private pos: Vec3 = new Vec3();
    private des: Vec3 = new Vec3();
    private lastPos = v3();

    targetPos: Vec3;

    failNextPoison = false;

    protected onLoad(): void {
        super.onLoad();
        // Events.on(EventName.JsonCompleted, this.Init, this);
        let collider = this.core.getComponent(Collider);
        collider.on('onControllerTriggerEnter', this.onControllerTriggerEnter, this);
        collider.on('onControllerTriggerExit', this.onControllerTriggerExit, this);
        collider = this.core.children[0].getComponent(Collider);
        collider.on('onTriggerEnter', this.onTriggerEnter, this);
        this.mapIcons = this.node.children[2];
        // Events.on(EventName.AIRestartNextPoison, () => { this.failNextPoison = false; }, this);
        Events.on(EventName.Finish, () => {
            this.StateSwitch(AIMoveState.Idle);
            this.unscheduleAllCallbacks();
        }, this);
    }

    start() {
        // return;
        // this.scheduleOnce(() => { this.StateSwitch(AIMoveState.Patrol); }, randomRange(0, 3));
    }

    update(deltaTime: number) {
        if (GameUI.Instance.isFinish && this.moveState != AIMoveState.Idle) return this.StateSwitch(AIMoveState.Idle);
        this._updateCount++;

        // 根据LOD级别决定更新频率
        if (this._lodLevel === 0 || this._updateCount % Math.pow(2, this._lodLevel) === 0) {
            this.PoisonCheck();
            this.CheckMove(deltaTime * (this._lodLevel + 1));
            this.CheckDir();
            this.Move(deltaTime * (this._lodLevel + 1)); // 调整deltaTime以补偿跳过的帧
            if (this.enemyTarget?.core) this.core.lookAt(this.enemyTarget.core.getWorldPosition(), this.core.up);
        }

        // 每秒更新一次LOD级别
        if (this._updateCount % 60 === 0) {
            this.updateLOD();
        }
    }

    protected onDestroy(): void {
        Events.targetOff(this);
    }

    InitModel(): void {
        let parent = this.node.children[0];
        if (parent.children[0]) PoolManager.PutNode(parent.children[0]);
        let num = randomRangeInt(0, InGameManager.Instance.gamerPrefabs.length);
        let model: Node = PoolManager.GetNodeByPrefab(InGameManager.Instance.gamerPrefabs[num], parent);
        model.layer = 1 << 3;
        let triggers = model.getComponentsInChildren(BodyTrigger);
        triggers.forEach((value) => {
            value.controller = this;
            value.getComponent(RigidBody).group = 1 << 2;
        });
        this.headwears = NodeUtil.GetNode("toutao", model);
        this.wings = NodeUtil.GetNode("chibang", model);
        // this.beibao = NodeUtil.GetNode("beibao", model);
        this.wings.active = true;
        // this.beibao.active = false;
        if (this.headwears.children[0]) PoolManager.PutNode(this.headwears.children[0]);
        if (this.wings.children[0]) PoolManager.PutNode(this.wings.children[0]);
        let data = randomRangeInt(-1, InGameManager.itemsJson.Adorn[1].length);
        if (data >= 0) {
            let head = InGameManager.itemsJson.Adorn[1][data];
            PoolManager.GetNodeByBundle("MainPack", head.prefab, this.headwears);
        }
        data = randomRangeInt(-1, InGameManager.itemsJson.Adorn[2].length);
        if (data >= 0) {
            let wing = InGameManager.itemsJson.Adorn[2][data];
            PoolManager.GetNodeByBundle("MainPack", wing.prefab, this.wings);
        }
        this.SetLayer(model);
        this.animationController = this.node.children[0].children[0].getComponent(animation.AnimationController);
        this.backPack.Init();
    }

    SetLayer(target: Node) {
        for (let i = 0; i < target.children.length; i++) {
            const element = target.children[i];
            element.layer = 1 << 3;
            if (element.children.length > 0) this.SetLayer(element);
        }
    }

    Restart(pos: Vec3) {
        this.isDead = false;
        this.Hp = 100;
        this.InitModel();
        this.animationController.setValue("IsDead", false);
        this.Init();
        this.scheduleOnce(() => { this.isInit = true; });
        this.scheduleOnce(() => { this.StateSwitch(AIMoveState.Patrol, pos); }, 1);
        this.schedule(this.CheckTarget, randomRange(1, 2));
        this.schedule(this.PosCheck, 1);
    }

    StateSwitch(state?: AIMoveState, patrolPos: Vec3 = null) {
        this.targetPos = patrolPos;
        if (state != undefined) this.moveState = state;
        switch (this.moveState) {
            case AIMoveState.Idle:
            case AIMoveState.Die:
                this.stopMove();
                break;
            //禁用找箱子
            // case AIMoveState.Search:
            //     if (!InGameManager.Instance.moveAgent(this, this.boxTarget.getWorldPosition(pos))) this.StateSwitch(AIMoveState.Patrol);
            //     break;
            // case AIMoveState.NextPoison:
            //     PoisonManager.Instance.nextPoison.getWorldPosition(pos);
            //     let pos2 = pos.clone();
            //     pos2.y = 100;
            //     let ray = new geometry.Ray;
            //     geometry.Ray.fromPoints(ray, pos2, pos);
            //     if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0, 100, false)) {
            //         const result = PhysicsSystem.instance.raycastClosestResult;
            //         pos = result.hitPoint;
            //     }
            //     if (!InGameManager.Instance.moveAgent(this, pos)) {
            //         this.failNextPoison = true;
            //         this.CheckMoveState();
            //     }
            //     break;
            case AIMoveState.Atk:
                this.AtkNextPos();
                break;
            case AIMoveState.Patrol:
                this.Patrol();
                break;
        }
    }

    ActionSwitch(state?: AIActionState) {
        if (state != undefined) this.actionState = state;
        switch (this.actionState) {
            case AIActionState.Idle:
            case AIActionState.Die:
                break;
            case AIActionState.Atk:
                break;
            case AIActionState.Heal:
                break;
        }
        this.ReadyShoot();
    }

    PosCheck() {
        if (this.moveState == AIMoveState.Idle || this.moveState == AIMoveState.Die) return;
        if (!this.isInit) return;
        let pos = this.node.getWorldPosition();
        if (Vec3.distance(pos, this.lastPos) < 1) this.StateSwitch();
        this.lastPos.set(pos);
    }

    PoisonCheck() {
        if (this.isDead) return;
        if (PoisonManager.Instance.CheckInPoison(this.node.getWorldPosition())) {
            if (!PoisonManager.Instance.CheckInNext(this.node.getWorldPosition()) && PoisonManager.Instance.time == 0) this.CheckMoveState();
            // if (this.moveState == AIMoveState.NextPoison && this.boxTarget) this.CheckMoveState();
        }
        // else if (this.moveState != AIMoveState.NextPoison && !this.failNextPoison) this.CheckMoveState();
        else {
            let pos = this.node.getWorldPosition();
            if (Math.abs(pos.x) > 500 || Math.abs(pos.z) > 500) return this.Hp = 0;
            if (!this.targetPos) this.StateSwitch(AIMoveState.Patrol, InGameManager.Instance.player.getWorldPosition());
        }
        let pos = this.node.getWorldPosition();
        pos.y = 0;
        let playerPos = InGameManager.Instance.player.getWorldPosition();
        playerPos.y = 0;
        let dis = Vec3.distance(pos, playerPos);
        if (dis > 100) {
            let ray = new geometry.Ray;
            geometry.Ray.fromPoints(ray, this.core.getWorldPosition(pos), InGameManager.Instance.cameraNode.getWorldPosition(playerPos));
            dis = Vec3.distance(pos, playerPos);
            if (PhysicsSystem.instance.raycastClosest(ray, dis, 0xffffffff, false)) InGameManager.Instance.Respawn(this);
        }
    }

    AtkNextPos() {
        if (this.isDead) return;
        if (!this.isInit) return;
        if (!this.enemyTarget) {
            // if (this.boxTarget) this.StateSwitch(AIMoveState.Search);
            // else 
            this.StateSwitch(AIMoveState.Patrol);
            return;
        }
        let enemyPos = this.enemyTarget.node.getWorldPosition();
        if (!InGameManager.Instance.moveAgent(this, enemyPos)) {
            // if (this.boxTarget) this.StateSwitch(AIMoveState.Search);
            // else 
            this.StateSwitch(AIMoveState.Patrol);
        }
    }

    Patrol() {
        if (this.isDead) return;
        if (!this.isInit) return;
        if (this.targetPos) {
            if (!InGameManager.Instance.moveAgent(this, this.targetPos)) {
                this.StateSwitch(AIMoveState.Idle);
                this.scheduleOnce(() => { this.CheckMoveState(); }, 3);
            }
        }
        else {
            let pos = this.node.getWorldPosition();
            pos.x += randomRange(-25, 25);
            pos.z += randomRange(-25, 25);
            let pos2 = pos.clone();
            pos2.y = 100;
            let ray = new geometry.Ray;
            geometry.Ray.fromPoints(ray, pos2, pos);
            if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0, 100, false)) {
                const result = PhysicsSystem.instance.raycastClosestResult;
                pos.y = result.hitPoint.y;
            }
            if (!InGameManager.Instance.moveAgent(this, pos)) {
                this.StateSwitch(AIMoveState.Idle);
                this.scheduleOnce(() => { this.CheckMoveState(); }, 3);
            }
        }
    }

    CheckTarget() {
        if (this.isDead) return;
        if (!this.isInit) return;
        let ray = new geometry.Ray;
        if (!this.enemyTarget) {
            this.allEnemyTargets = this.allEnemyTargets.filter((value, index, array) => { if (value.Hp > 0) return value; });
            if (this.allEnemyTargets.length > 0) {
                if (this.allEnemyTargets.length > 1) this.allEnemyTargets = this.allEnemyTargets.sort((a, b) => { return Vec3.distance(a.core.getWorldPosition(), this.core.getWorldPosition()) - Vec3.distance(b.core.getWorldPosition(), this.core.getWorldPosition()); });
                for (let i = 0; i < this.allEnemyTargets.length; i++) {
                    const element = this.allEnemyTargets[i];
                    geometry.Ray.fromPoints(ray, this.core.getWorldPosition(), element.core.getWorldPosition());
                    if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2 | 1 << 5, 100, true)) {
                        const result = PhysicsSystem.instance.raycastClosestResult;
                        const target = result.collider.getComponent(BodyTrigger);
                        if (target && target.controller == element) {
                            this.enemyTarget = element;
                            break;
                        }
                    }
                }
            }
        }
        // if (!this.boxTarget) {
        //     if (this.allBoxTargets.length != 0) {
        //         this.allBoxTargets = this.allBoxTargets.filter((value, index, array) => { return PoisonManager.Instance.CheckInPoison(value.getWorldPosition()); });
        //         if (this.moveState == AIMoveState.NextPoison) {
        //             let targets = this.allBoxTargets.filter((value, index, array) => { return PoisonManager.Instance.CheckInNext(value.getWorldPosition()); });
        //             if (targets) {
        //                 targets.sort((a, b) => { return Vec3.distance(a.getWorldPosition(), this.core.getWorldPosition()) - Vec3.distance(b.getWorldPosition(), this.node.getWorldPosition()); });
        //                 this.boxTarget = targets[0];
        //             }
        //         }
        //         else {
        //             this.allBoxTargets.sort((a, b) => { return Vec3.distance(a.getWorldPosition(), this.core.getWorldPosition()) - Vec3.distance(b.getWorldPosition(), this.node.getWorldPosition()); });
        //             this.boxTarget = this.allBoxTargets[0];
        //         }
        //     }
        // }
        this.ActionSwitch(this.enemyTarget ? AIActionState.Atk : AIActionState.Idle);
        let pos = v3();
        let dis = Vec3.distance(this.node.getWorldPosition(pos), InGameManager.Instance.player.getWorldPosition(pos));
        if (dis > 70) this.StateSwitch(AIMoveState.Patrol, pos);
    }

    CheckMoveState() {
        if (this.moveState == AIMoveState.Die) return;
        if (!this.isInit) return;
        // if (!this.failNextPoison && this.moveState != AIMoveState.NextPoison) {
        //     if (!PoisonManager.Instance.CheckInPoison(this.node.getWorldPosition())) return this.StateSwitch(AIMoveState.NextPoison);
        //     if (!PoisonManager.Instance.CheckInNext(this.node.getWorldPosition()) && PoisonManager.Instance.time == 0) return this.StateSwitch(AIMoveState.NextPoison);
        // }
        // if (!this.failNextPoison && (!PoisonManager.Instance.CheckInPoison(this.node.getWorldPosition()) || (!PoisonManager.Instance.CheckInNext(this.node.getWorldPosition()) && PoisonManager.Instance.time == 0))) return;
        let weapon = this.backPack.CurWeapon;
        // if (this.moveState != AIMoveState.Search && this.boxTarget) this.StateSwitch(AIMoveState.Search);
        // else 
        if (this.moveState != AIMoveState.Atk && weapon && this.enemyTarget) this.StateSwitch(AIMoveState.Atk);
        // else if (this.moveState != AIMoveState.Search && this.BoxCheck()) this.StateSwitch(AIMoveState.Search);
        else if (this.moveState != AIMoveState.Patrol) this.StateSwitch(AIMoveState.Patrol);
        else {
            this.StateSwitch(AIMoveState.Idle);
            this.scheduleOnce(() => { this.StateSwitch(AIMoveState.Patrol) }, 3);
        }
    }

    BoxCheck(): boolean {
        let boxs = [...InGameManager.Instance.boxs.children];
        boxs = boxs.filter((value, index, array) => { if (PoisonManager.Instance.CheckInPoison(value.getWorldPosition())) return value });
        boxs = boxs.sort((a, b) => { return Vec3.distance(a.getWorldPosition(), this.node.getWorldPosition()) - Vec3.distance(b.getWorldPosition(), this.node.getWorldPosition()) });
        if (boxs && boxs.length > 0) {
            this.boxTarget = boxs[0];
            this.allBoxTargets.push(boxs[0]);
            return true;
        }
        return false;
    }

    EnemyCheck() {
        if (this.allEnemyTargets.indexOf(this.enemyTarget) == -1) this.enemyTarget == null;
    }

    CheckMove(deltaTime: number): void {
        if (this.isDead) return;
        if (!this.isInit) return;
        if (this.move) {
            //播放动画
            this.pos.set(this.node.position)
            if (this.currstep != this.step) {
                this.des.set(this._path[this.step]);
                this.currstep = this.step;
            }
            if (!this.enemyTarget) this.rotateRole(this.pos, this.des);
            if (nUtils.CheckArrived(this.pos, this.des)) this.changeStep();
        }
        if (this.enemyTarget) this.rotateRole(this.pos, this.enemyTarget.node.getWorldPosition());
    }

    CheckDir() {
        if (this.isDead) return;
        if (!this.isInit) return;
        if (!this.move) return;
        let desDir = v3();
        Vec3.subtract(desDir, this.des, this.node.getWorldPosition());
        desDir.y = 0;
        desDir.normalize();
        let angle = this.node.eulerAngles.y;
        angle = toRadian(angle);
        let dir = v3();
        Vec3.rotateY(dir, desDir, Vec3.ZERO, -angle);
        dir = dir.normalize();
        this.Control_x = dir.x;
        this.Control_z = -dir.z;
        this.deltaNor.x = dir.x;
        this.deltaNor.y = -dir.z;
    }

    changeStep(): void {
        this.step++
        if (this.step == this._path.length) {
            //站立
            this.deltaNor.x = 0;
            this.deltaNor.y = 0;
            this.Control_x = 0;
            this.Control_z = 0;
            this.move = false;
            this.MapStep(false);
            this.CheckMoveState();
        }
    }

    /**
     * @Date: 2022-02-1 01:26:04
     * @LastEditors: iwae
     * @description: 水平方向面对目标
     * @param {Vec3} pos 转向节点位置
     * @param {Vec3} target 目标位置
     */
    rotateRole(pos: Vec3, target: Vec3): void {
        this.roleAngel = (Math.atan2(target.x - pos.x, target.z - pos.z) * (180 / Math.PI) + 180) % 360;
        //角色转动的角度,相对Z轴，逆时针为正方向
        this.node.setRotationFromEuler(0, this.roleAngel);
    }

    startMove(path): void {
        this.stopMove();
        this._path = path;
        this.move = true;
        this.MapStep(true);
    }

    stopMove(): void {
        this.move = false;
        this.MapStep(false);
        this._path.length = 0;
        this.step = 0;
        this.Control_x = 0;
        this.Control_z = 0;
        this.currstep = null;

    }

    Init() {
        this.backPack.ContainBox = null;
        this.enemyTarget = null;
        let type = randomRangeInt(0, 5);
        let num = randomRangeInt(0, 3);
        this.backPack.GetItem(Gun.Init(type, num));
        this.backPack.GetItem(Ammo.Init(type));
        type = randomRangeInt(-1, 1);
        if (type > 0) this.backPack.GetItem(Armor.Init(type));
        type = randomRangeInt(2, 4);
        if (type > 2) this.backPack.GetItem(Armor.Init(type));
        // this.enemyTarget = PlayerController.Instance;
        // this.scheduleOnce(() => { this.ShootStart(); }, 1);
    }

    ReadyShoot() {
        if (!this.animationController) return;
        if (this.isDead) return;
        if (!this.isInit) return;
        let isShoot = this.animationController.getValue("IsShoot");
        if (!this.enemyTarget || this.actionState != AIActionState.Atk) {
            if (isShoot) this.ShootEnd();
            return;
        }
        let ray = new geometry.Ray;
        geometry.Ray.fromPoints(ray, this.core.getWorldPosition(), this.enemyTarget.core.getWorldPosition());
        if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2 | 1 << 5, 100)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            const collider = result.collider;
            const body = collider.getComponent(BodyTrigger);
            if (body) { if (!isShoot) this.ShootStart(); }
            else { if (isShoot) this.ShootEnd(); }
        }
        else { if (isShoot) this.ShootEnd(); }
    }

    Shoot(): void {
        if (this.isDead) return;
        if (!this.isInit) return;
        if (this.backPack.CurWeapon instanceof Gun && this.backPack.CurWeapon.curAmmo > 0) this.MapShootOn();
        super.Shoot();
        if (this.enemyTarget && this.enemyTarget.Hp == 0) {
            let num = this.allEnemyTargets.indexOf(this.enemyTarget);
            if (num != -1) this.allEnemyTargets.splice(num, 1);
            this.enemyTarget = null;
        }
    }

    ShootBullet(): void {
        if (this.isDead) return;
        if (!this.isInit) return;
        if (this.backPack.weaponSlots.length == 0) return;
        if (this.backPack.weaponSlots[4].children.length == 0) return;
        let oPos = this.backPack.weaponSlots[4].children[0].children[0].getWorldPosition();
        let forward = this.core.forward.clone().normalize();
        let up = this.core.up.clone().normalize();
        let right = this.core.right.clone().normalize();
        let dir = this.getRandomShootDirectionSimple(forward, up, right);
        let dPos = v3();
        Vec3.add(dPos, oPos, dir.multiplyScalar(1000));
        let ray = new geometry.Ray();
        geometry.Ray.fromPoints(ray, oPos, dPos);
        if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0 | 1 << 2 | 1 << 5, 1000)) {
            const result = PhysicsSystem.instance.raycastClosestResult;
            const point = result.hitPoint.clone();
            InGameManager.Instance.ShowTrail(oPos, point);
            const rig = result.collider.attachedRigidBody;
            if (rig && (rig.group == 1 << 2 || rig.group == 1 << 5)) {
                let body = rig.getComponent(BodyTrigger);
                body.Damage(this.backPack.CurWeapon, this);
                if (body.type != 2) InGameManager.Instance.ShowBlood(point);
                else InGameManager.Instance.ShowFirework(point);
            }
        }
        else InGameManager.Instance.ShowTrail(oPos, dPos);
    }

    MapShootOn() {
        if (this.isDead) return;
        if (!this.isInit) return;
        this.unschedule(this.MapShootOff);
        this.mapIcons.children[0].active = true;
        this.scheduleOnce(this.MapShootOff, 0.5);
        this.mapIcons.children[1].active = false;
    }

    MapShootOff() {
        this.mapIcons.children[0].active = false;
        this.mapIcons.children[1].active = this.move;
    }

    MapStep(active: boolean) {
        this.mapIcons.children[1].active = active;
    }

    Damage(part: BodyTrigger, weapon: Weapon, source: GamerController): void {
        super.Damage(part, weapon, source);
        let num = this.allEnemyTargets.indexOf(source);
        if (num == -1) this.allEnemyTargets.push(source);
        if (!this.enemyTarget) this.enemyTarget = source;
    }

    Die(): void {
        this.ShootEnd();
        super.Die();
        this.StateSwitch(AIMoveState.Die);
        this.ActionSwitch(AIActionState.Die);
        this.ShootEnd();
        this.move = false;
        this.MapShootOff();
        this.unscheduleAllCallbacks();
        let box = ItemBox.Init(this.node.getWorldPosition());
        box.items = [...this.backPack.items];
        let armors = this.backPack.armors;
        for (let i = 0; i < armors.length; i++) {
            const element = armors[i];
            if (element) box.items.push(element);
        }
        this.backPack.Clear();
        this.scheduleOnce(() => {
            this.isInit = false;
            PoolManager.PutNode(this.node);
        }, 2);
    }

    onControllerTriggerEnter(event: CharacterControllerContact) {
        const other = event.characterController;
        if (other == this.characterController) return;
        if (other.group == 1 << 1 && this.allEnemyTargets.indexOf(other.getComponent(GamerController)) == -1) {
            this.allEnemyTargets.push(other.getComponent(GamerController));
            this.CheckTarget();
            if (other.getComponent(GamerController) == this.enemyTarget) this.unschedule(this.EnemyCheck);
        }
    }

    onControllerTriggerExit(event: CharacterControllerContact) {
        const other = event.characterController;
        let num = this.allEnemyTargets.indexOf(other.getComponent(GamerController));
        if (other.group == 1 << 1 && num != -1) {
            this.allEnemyTargets.splice(num, 1);
            if (this.enemyTarget == other.getComponent(GamerController)) this.scheduleOnce(this.EnemyCheck, 3);
        }
    }

    onTriggerEnter(event: ITriggerEvent) {
        let other = event.otherCollider.attachedRigidBody;
        if (other && other.group == 1 << 3 && this.allBoxTargets.indexOf(other.node) == -1) this.allBoxTargets.push(other.node.parent);
    }

}

enum AIMoveState {
    Idle,
    // Search,
    // NextPoison,
    Atk,
    Patrol,
    Die
}

enum AIActionState {
    Idle,
    Atk,
    Heal,
    Die
}
